Fix flaky query history reveal test via stable TreeItem ids#4468
Conversation
The query history tree data provider never set an id on its TreeItems, so
VS Code identified tree nodes by their label plus their position in the
list. When multiple history items share the same label, that positional
identity is ambiguous, so treeView.reveal could resolve to the wrong item
or fail outright ("Data tree node not found") when the list was re-sorted
or refreshed.
This mainly affects tests. The default label format includes the query
start time, so real query history labels are effectively unique and users
are unlikely to hit this (the only exception being a user who customises
the label format to something non-unique). In query-history-manager.test.ts
the label format is overridden to just the query name, producing identical
labels; this made the "should not change the selection" cases flaky,
reproducing locally around 50% of the time and disappearing entirely after
this change. Because the impact is effectively test-only, no changelog
entry is added.
Set a stable, unique id on each tree item so reveal resolves
deterministically regardless of duplicate labels or sort order. The id must
be unique per item: getQueryId is not sufficient on its own because a
multi-query run produces several local-query items that share the same
initialInfo.id, so for local queries we also include the per-result output
base name (VS Code de-duplicates nodes that share an id, which would
otherwise break reveal/selection for the colliding items).
Co-authored-by: Copilot <[email protected]>
Copilot-Session: f0809f2f-2c92-4acf-8f51-468c5e79c14a
308e76c to
e65dfe8
Compare
There was a problem hiding this comment.
Pull request overview
Adds stable query-history tree identifiers to prevent flaky reveal and selection behavior.
Changes:
- Assigns unique
TreeItem.idvalues. - Disambiguates multi-query local results using
outputBaseName.
Show a summary per file
| File | Description |
|---|---|
extensions/ql-vscode/src/query-history/history-tree-data-provider.ts |
Adds stable IDs for local and variant-analysis history items. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 1/1 changed files
- Comments generated: 2
- Review effort level: Medium
| return `local:${element.initialInfo.id}:${ | ||
| element.completedQuery?.query.outputBaseName ?? "" | ||
| }`; |
There was a problem hiding this comment.
My understanding is that outputBaseName was introduced the same time as multi-query support, so there cannot be any legacy multi-query entry without outputBaseName.
| return `local:${element.initialInfo.id}:${ | ||
| element.completedQuery?.query.outputBaseName ?? "" | ||
| }`; |
There was a problem hiding this comment.
This is mainly a test flakiness fix, and I don't think the behavior nuance is important enough to justify having its own tests.
josefs
left a comment
There was a problem hiding this comment.
LGTM.
A more natural home for the getTreeItemId might be in the query-history-info.ts module, but I don't have a strong opinion about that.
Summary
Fixes a flaky test in
query-history-manager.test.ts(thehandleRemoveHistoryItem› "should not change the selection" cases), which was failing on CI (e.g. this windows-latest run).Root cause
HistoryTreeDataProvider.getTreeItemnever set aTreeItem.id. When noidis provided, VS Code identifies tree nodes by their label plus their position in the list (<parent>/<index>:<label>). When multiple history items share the same label, that positional identity is ambiguous, sotreeView.reveal(item)can resolve to the wrong item or fail outright (Data tree node not found) whenever the list is re-sorted or refreshed.The test overrides the label format to just
${queryName}, so all six mock variant-analysis items render with the identical labela-query-name (javascript). Combined with the randomised sort order (executionStartTimeisfaker.number.int()),reveal(selected)intermittently selected the wrong item, sogetCurrent()no longer equalledselected.Impact
Mainly test-only. The production default label format is
${queryName} on ${databaseName} - ${status} ${resultCount} [${startTime}], which includes the start time — so real history labels are effectively unique and users are very unlikely to hit this. The one exception is a user who customisescodeQL.queryHistory.formatto something non-unique. Because the impact is effectively test-only, no changelog entry is added.Fix
Set a stable, unique
idon each tree item sorevealresolves deterministically regardless of duplicate labels or sort order.The id must be unique per item.
getQueryIdalone is not sufficient: a multi-query run (a query suite / multiple.qlfiles) produces several local-query history entries that are cloned from the sameinitialInfoand therefore share the sameinitialInfo.id(seecompleteQueriesinquery-history-manager.ts). Since VS Code de-duplicates nodes that share anid— which would break reveal/selection for all but one of the colliding rows — the id for local queries also includes the per-resultoutputBaseName, which is distinct for each entry (and is persisted, so this also disambiguates already-saved histories). Variant-analysis ids are already unique.Validation
tsc --noEmitandeslintpass.